package com.tools.common.security.id;

import com.tools.common.object.Note;

import java.security.SecureRandom;
import java.util.Random;
import java.util.TreeSet;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 自定义数字 ID 生成器实例
 * */
@Note("自定义数字 ID 生成器实例")
public final class StringID {

    @Note("线程 + 密码安全的随机数生成器对象")
    private static final SecureRandom DEFAULT_RANDOM_GENERATOR = new SecureRandom();

    private static final char[] DEFAULT_CHAR_ARRAY = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();;

    @Note("字符串 ID 的默认长度")
    private static final int DEFAULT_STRING_ID_LENGTH = 21;

    @Note("16 进制字符模板")
    private static char[] SIXTEEN_BINARY_TEMPLATE;

    /* *******************************************************************************************
     *
     *         构造器
     *
     * *******************************************************************************************
     * */

    public StringID() {}

    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("使用默认的字符模板数组，获取长度为默认长度的字符串 ID 返回")
    public String newStringID() {
        return newStringID(DEFAULT_STRING_ID_LENGTH, DEFAULT_CHAR_ARRAY);
    }


    @Note("使用默认的字符模板数组，获取长度为 size 的字符串 ID 返回")
    public String newStringID(int length) {
        return newStringID(length, DEFAULT_CHAR_ARRAY);
    }


    @Note("使用指定的字符模板数组，获取长度为 size 的字符串 ID 返回")
    public String newStringID(int length, char[] charTemplate){
        length = (length < 1) ? DEFAULT_STRING_ID_LENGTH : length;
        if(charTemplate == null || charTemplate.length == 0) {
            charTemplate = DEFAULT_CHAR_ARRAY;
        }
        StringBuilder builder = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            int index = DEFAULT_RANDOM_GENERATOR.nextInt(charTemplate.length);
            char c = charTemplate[index];
            builder.append(c);
        }
        return builder.toString();
    }

    @Note("使用默认的随机数生成器、默认的字符模板数组、默认的长度生成一个 NanoID 返回")
    public String newNanoID() {
        return newNanoID(DEFAULT_RANDOM_GENERATOR, DEFAULT_CHAR_ARRAY, DEFAULT_STRING_ID_LENGTH);
    }

    @Note("返回一个指定长度的 NanoID")
    public String newNanoID(int length) {
        return newNanoID(DEFAULT_RANDOM_GENERATOR, DEFAULT_CHAR_ARRAY, length);
    }


    @Note("使用指定的随机数生成器、指定的字符模板数组、指定的长度生成一个 NanoID 返回")
    public String newNanoID(Random random, char[] charArrayTemplate, int length) {
        if (random == null) {
            random = DEFAULT_RANDOM_GENERATOR;
        }
        if (charArrayTemplate == null || charArrayTemplate.length < 1 || charArrayTemplate.length > 255) {
            charArrayTemplate = DEFAULT_CHAR_ARRAY;
        }
        if(length < 1) {
            length = DEFAULT_STRING_ID_LENGTH;
        }
        int mask = (2 << (int) Math.floor(Math.log((charArrayTemplate.length - 1)) / Math.log(2.0D))) - 1;
        int step = (int) Math.ceil(1.6D * (double) mask * (double) length / (double) charArrayTemplate.length);
        StringBuilder sb = new StringBuilder();
        while (true) {
            byte[] bytes = new byte[step];
            random.nextBytes(bytes);
            for (int i = 0; i < step; i++) {
                int alphabetIndex = bytes[i] & mask;
                if(alphabetIndex < charArrayTemplate.length) {
                    sb.append(charArrayTemplate[alphabetIndex]);
                    if(sb.length() == length) {
                        return sb.toString();
                    }
                }
            }
        }
    }



    @Note("获取指定长度的十六进制的随机字符串")
    public String new16BinaryID(int length) {
        if(length <= 0) return "";
        private_lazyLoadSixteenBinaryTemplate();
        StringBuilder builder = new StringBuilder();
        ThreadLocalRandom random = ThreadLocalRandom.current();
        int bound = SIXTEEN_BINARY_TEMPLATE.length;
        for (int i = 0; i < length; i++) {
            int index = random.nextInt(bound);
            builder.append(SIXTEEN_BINARY_TEMPLATE[index]);
        }
        return builder.toString();
    }

    /* *******************************************************************************************
     *
     *          批量生成字符串 ID
     *
     * *******************************************************************************************
     * */

    @Note("批量生成 number 个默认长度【21】、默认模板的 NanoID，并按自然排序排好返回")
    public TreeSet<String> newBatchNanoID(int number) {
        if(number <= 0) return new TreeSet<>();
        TreeSet<String> idSet = new TreeSet<>();
        for (int i = 0; i < number; i++) {
            idSet.add(this.newNanoID());
        }
        return idSet;
    }


    @Note("批量生成 number 个指定长度、默认模板的 NanoID，并按自然排序排好返回")
    public TreeSet<String> newBatchNanoID(int number, int length) {
        if(number <= 0) return new TreeSet<>();
        TreeSet<String> idSet = new TreeSet<>();
        for (int i = 0; i < number; i++) {
            idSet.add(this.newNanoID(length));
        }
        return idSet;
    }

    @Note("批量生成 number 个由指定随机数生成器、字符模板、长度制造的 NanoID，并按自然排序排好返回")
    public TreeSet<String> newBatchNanoID(int number, Random random, char[] template, int length) {
        if(number <= 0) return new TreeSet<>();
        TreeSet<String> idSet = new TreeSet<>();
        for (int i = 0; i < number; i++) {
            idSet.add(this.newNanoID(random, template, length));
        }
        return idSet;
    }


    @Note("批量生成 number 个默认长度【21】、默认模板的字符串 ID，并按自然排序排好返回")
    public TreeSet<String> newBatchStringID(int number) {
        if(number <= 0) return new TreeSet<>();
        TreeSet<String> idSet = new TreeSet<>();
        for (int i = 0; i < number; i++) {
            idSet.add(this.newStringID());
        }
        return idSet;
    }


    @Note("批量生成 number 个指定长度、默认模板的字符串 ID，并按自然排序排好返回")
    public TreeSet<String> newBatchStringID(int number, int idLength) {
        if(number <= 0) return new TreeSet<>();
        TreeSet<String> idSet = new TreeSet<>();
        for (int i = 0; i < number; i++) {
            idSet.add(this.newStringID(idLength));
        }
        return idSet;
    }

    @Note("批量生成 number 个指定长度、指定的字符模板的字符串 ID，并按自然排序排好返回")
    public TreeSet<String> newBatchStringID(int number, int idLength, char[] template) {
        if(number <= 0) return new TreeSet<>();
        TreeSet<String> idSet = new TreeSet<>();
        for (int i = 0; i < number; i++) {
            idSet.add(this.newStringID(idLength, template));
        }
        return idSet;
    }

    @Note("批量生成 number 个指定长度的 16 进制字符串 ID，并按自然排序排好返回")
    public TreeSet<String> newBatch16BinaryID(int number, int idLength) {
        if(number <= 0) return new TreeSet<>();
        TreeSet<String> idSet = new TreeSet<>();
        for (int i = 0; i < number; i++) {
            idSet.add(this.new16BinaryID(idLength));
        }
        return idSet;
    }


    @Note("懒加载 16 进制字符模板")
    private static void private_lazyLoadSixteenBinaryTemplate() {
        if(SIXTEEN_BINARY_TEMPLATE != null) return;
        synchronized (IDKit.class) {
            if(SIXTEEN_BINARY_TEMPLATE != null) return;
            SIXTEEN_BINARY_TEMPLATE = "0123456789abcdef".toCharArray();
        }
    }
}
